package cn.xiaomagege.platform.component;

import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.*;

@Component
public class LuceneSearch {
    
    // Lucene索引文件路径
    public static String dir;
    
    @Value("${lucence-dic}")
    public void setDir(String lucenceDir) {
        dir = lucenceDir;
    }
    
    static Analyzer analyzer = new IKAnalyzer();
    
    public static void write(Document document) throws IOException {
        Directory directory = FSDirectory.open(Paths.get(dir)); 
        Analyzer analyzer = new IKAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
        indexWriter.addDocument(document);
        indexWriter.commit();
        indexWriter.close();
    }
    
    public static void deleteIndexById(String id) throws IOException {
        Directory directory = FSDirectory.open(Paths.get(dir)); 
        Analyzer analyzer = new IKAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        IndexWriter writer = new IndexWriter(directory, indexWriterConfig);
        IndexReader reader = DirectoryReader.open(directory); 
        Query q = new TermQuery(new Term("id", id));
        writer.deleteDocuments(q); // 删除指定ID的Document
        writer.commit(); // 提交
        writer.close(); // 关闭
        reader.close(); // 关闭
    }
    
    public static void updateIndexById(BlogArticle blogArticle) throws IOException {
        Directory directory = FSDirectory.open(Paths.get(dir)); // 打开文件索引目录
        Analyzer analyzer = new IKAnalyzer();
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
        // 创建索引写入对象
        IndexWriter writer = new IndexWriter(directory, indexWriterConfig);
        Document doc = new Document();
        doc.add(new LongPoint("id", blogArticle.getId()));
        doc.add(new TextField("title", blogArticle.getTitle(), Field.Store.YES));
        doc.add(new TextField("marks", blogArticle.getMarks() == null ? "" : blogArticle.getMarks(), Field.Store.YES));
        doc.add(new TextField("text", blogArticle.getText() == null ? "" : blogArticle.getText(), Field.Store.YES));
        doc.add(new StoredField("href", blogArticle.getBlogChannel().getHref()));
        doc.add(new StoredField("show_pic", blogArticle.getShowPic() == null ? "" : blogArticle.getShowPic()));
        writer.updateDocument(new Term("id", blogArticle.getId().toString()), doc);
        writer.commit(); // 提交
        writer.close(); // 关闭
    }
    
    /**
     * 搜索
     * @param field 字段名称
     * @param value 搜索内容
     * @return 搜索结果
     * @throws Exception
     */
    public Map<String, Object> search(String[] field, String value, Page page) throws Exception {
        Map<String, Object> dataMap = new HashMap<>();
        Directory directory = FSDirectory.open(Paths.get(dir));
        DirectoryReader directoryReader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
        
        // 多字段查询解析器
        MultiFieldQueryParser parser = new MultiFieldQueryParser(field, analyzer);
        Query query = parser.parse(value);
        
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(query, BooleanClause.Occur.SHOULD);
        
        for(int i = 0; i < field.length; i++) {
            // 按词条搜索
            Query q = new TermQuery(new Term(field[i], value));
            builder.add(q, BooleanClause.Occur.SHOULD);
        }
        
        BooleanQuery finalQuery = builder.build();
        
        // TopDocs 搜索返回的结果
        TopDocs topDocs = indexSearcher.search(finalQuery, 100);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        
        // 高亮显示
        SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter("<span style='color: #FF5722;'>", "</span>");
        Highlighter highlighter = new Highlighter(htmlFormatter, new QueryScorer(query));
        
        List<Map<String, Object>> list = new ArrayList<>();
        int begin = (page.getPageNum() - 1) * page.getPageSize();
        int end = Math.min(begin + page.getPageSize(), scoreDocs.length);
        
        for(int i = begin; i < end; i++) {
            Map<String, Object> map = new HashMap<>();
            int docID = scoreDocs[i].doc;
            Document doc = indexSearcher.doc(docID);
            Long id = Long.valueOf(doc.get("id"));
            String title = doc.get("title");
            
            if(StringUtils.isNotBlank(title)) {
                TokenStream tokenStream = analyzer.tokenStream("title", new StringReader(title));
                title = highlighter.getBestFragment(tokenStream, title);
            }
            
            String text = doc.get("text");
            if(StringUtils.isNotBlank(text)) {
                TokenStream tokenStream = analyzer.tokenStream("text", new StringReader(text));
                text = highlighter.getBestFragment(tokenStream, text);
            }
            
            String marks = doc.get("marks");
            if(StringUtils.isNotBlank(marks)) {
                TokenStream tokenStream = analyzer.tokenStream("marks", new StringReader(marks));
                marks = highlighter.getBestFragment(tokenStream, marks);
            }
            
            String href = doc.get("href");
            String showPic = doc.get("showPic");
            
            // 评分
            Explanation explanation = indexSearcher.explain(query, docID);
            
            map.put("id", id);
            map.put("href", href);
            map.put("showPic", showPic);
            map.put("title", title);
            map.put("text", text);
            map.put("marks", marks);
            DecimalFormat fnum = new DecimalFormat("##0.00");
            map.put("percent", fnum.format(explanation.getValue()));
            list.add(map);
        }
        
        dataMap.put("data", list);
        dataMap.put("count", list.size());
        dataMap.put("total", scoreDocs.length);
        
        directoryReader.close();
        directory.close();
        
        return dataMap;
    }
}